;;; The vanilla box & arrow tutor

(defpackage :vanilla
  (:use :cl)
  (:nicknames :v)
  (:export :vanilla
           1-1-odd
           1-2-odd
           1-7-odd
           2-6-odd
           2-7-odd
           3-1
           3-2
           3-4
           3-5
           4-1
           4-2
           4-3))

(in-package :vanilla)

(defun box-callback (view event)
  (let ((vanilla (gwee:component-for-view view)))
    (bt:event-callback vanilla event)))

(gwee:define-component vanilla (bt:basic-trial)
  ()
  (:views
   (calc calculator:calculator)
   (p:diagram tv:tree-view
              :event-callback 'box-callback
              :height :maximum
              :width :maximum)
   (bt:last-state tv:tree-view
                  :height :maximum
                  :width :maximum)
   (last-diagram-text gwee:text-area :data "Last Diagram State" :width :maximum)
   (diagram-text gwee:text-area :data "Current Diagram State" :width :maximum)
   (bt:message gwee:column-layout
               :width :maximum
               :height 203
               :border-color (gwee:gray 0)
               :border-left nil
               :border-bottom nil
               :cell-gravity :top
               :gravity :bottom
               :margin 5)
   (bt:instruction-title-view gwee:text-area :data ""
                              :width :maximum
                              :font (gwee:open-font "sans.ttf" 24))
   (bt:instruction-body-view gwee:text-area :data "instructions go here"
                             :height :maximum
                             :width :maximum
                             :font (gwee:open-font "sans.ttf" 18))
   (hint gwee:button :height 25 :width :maximum :data "Hint")
   (back gwee:button :height 25 :width :maximum :data (string (code-char 8592)))
   (forward gwee:button :height 25 :width :maximum :data (string (code-char 8594)))
   (first-mistake gwee:button :height 25 :width :maximum :data "First Mistake")
   (next-problem gwee:button :height 25 :width :maximum :data "Next Problem")
   (reward gwee:text-area :width :maximum :data "")
   (remaining gwee:text-area :width :maximum :data ""))
  (:layouts
   (default gwee:row-layout
       (last-col diagram-col status-col)
     :width :maximum
     :height :maximum
     :padding 0
     :margin 10)
   (last-col gwee:column-layout
             (instruction-area last-area)
             :width :maximum
             :height :maximum)
   (last-area gwee:column-layout
              (last-diagram-text bt:last-state)
              :width :maximum
              :height :maximum
              :border-color (gwee:gray 0)
              :margin 5)
   ;; we need this so that the input keypad doesn't get moved by the column layout container
   (diagram-area gwee:column-layout
                 (diagram-text p:diagram)
                 :width :maximum
                 :height :maximum
                 :border-color (gwee:gray 0)
                 :border-left nil
                 :margin 5)
   (instruction-area gwee:column-layout
                     (bt:instruction-title-view bt:instruction-body-view)
                     :margin 5
                     :padding 5
                     :gravity :top
                     :border-color (gwee:gray 0)
                     :border-bottom nil
                     :width :maximum
                     :height 203)
   (navigate gwee:column-layout
             (hint back-forward first-mistake next-problem)
             :padding 5
             :margin 5
             :height :minimum
             :width :maximum)
   (back-forward gwee:row-layout
                 (back forward)
                 :padding 10
                 :height :minimum
                 :width :maximum)
   (status gwee:column-layout
           (reward remaining)
           :width :maximum
           :padding 5
           :margin 10)
   (buttons gwee:column-layout
            ()
            :width :maximum
            :height :minimum
            :padding 5
            :margin 5)
   (status-col gwee:column-layout
               (calc buttons navigate status)
               :border-color (gwee:gray 0)
               :border-left nil
               :gravity :top
               :width 158
               :padding 10
               :height :maximum)
   (diagram-col gwee:column-layout
                (bt:message diagram-area)
                :width :maximum
                :height :maximum))
  (:default-initargs
      :instruction-title "Solve the following problems"
    :instruction-body "If you get stuck, click the Hint button for a hint."))

(defmethod bt:make-operations ((trial vanilla))
  (list
   (make-instance 'o:operation
                  :name :evaluate
                  :transformations (o::evaluate-transformations))
   (make-instance 'o:operation
                  :name :invert
                  :transformations (o::invert-transformations))
   (make-instance 'o:operation
                  :name :combine
                  :transformations (append
                                    (o::basic-combine-transformations)
                                    (o::x-combine-transformations)
                                    (o::x-combine2-transformations)
                                    (o::5box-combine-transformations)
                                    (o::weird-combine-transformations)))
   (make-instance 'o:operation
                  :name :reorder
                  :transformations (o::reorder-transformations))
   (make-instance 'o:operation
                  :name :subtract
                  :transformations (o::subtract-transformations))))

(defmethod bt:make-operation-views ((state vanilla))
  (loop for i in (p:operations state)
     collect (make-instance 'gwee:button
                            :height 25
                            :width :maximum
                            :data (string-capitalize (o:name i))
                            :click-callback (let ((op (o:name i))) (exp:dispatch-event-fn exp:*experiment* state exp:click-event :source op)))))

(defmethod exp:state-init (exp (state vanilla))
  (labels ((wire (widget source)
             (setf (gwee:click-callback (slot-value state widget))
                   (exp:dispatch-event-fn exp state exp:click-event :source source))))
    (with-slots (buttons reward remaining) state
      (setf (gwee:view-data reward) (format nil "$~,2f" (/ (exp:reward exp) 100))
            (gwee:view-data remaining) (when (and exp:*subsequence-nth* exp:*subsequence-length*)
                                         (format nil "Problem ~d of ~d" (1+ exp:*subsequence-nth*) exp:*subsequence-length*))
            (gwee:view-subviews buttons) (bt:make-operation-views state)
;;             (gwee:view-height buttons) (gwee::layout-major-size buttons)
            )
      (wire 'next-problem :next-problem)
      (wire 'back :back)
      (wire 'forward :forward)
      (wire 'first-mistake :first-mistake)
      (wire 'hint :hint))
    ;; FIXME: all the layouts need to be synced because we call
    ;; refresh-diagram and refresh-tree manually which need to know
    ;; the size of the box-view. Why can't we do that automatically?
    ;;(setf (gwee:view-needs-sync-p state) t)
    (gwee:sync-views)))

(defun sandbox ()
  (exp:run-experiment
   `((:1-1 ,@(1-1-odd))
     (:1-2 ,@(1-2-odd))
     (:1-7 ,@(1-7-odd))
     (:2-6 ,@(2-6-odd))
     (:2-7 ,@(2-7-odd))
     (:3-1 ,@(3-1))
     (:3-2 ,@(3-2))
     (:3-4 ,@(3-4))
     (:3-5 ,@(3-5))
     (:4-1 ,@(4-1))
     (:4-2 ,@(4-2))
     (:4-3 ,@(4-3)))
   :width 1220
   :height 730))

(defun vanilla-test ()
  (exp:run-experiment `((:session1
                         ,(p '(* (+ 5 7) 8) '(96) 2 t)
                         ,(p '(= (+ (* 3 x) 55) (* 8 x)) '(= x 11) 7 t)
                         (exp:end))
                        (:turd-session
                         (vanilla :tree ,(o:generate-tree nil '(s (a 10
                                                                 (o (a +)
                                                                  (a 3)
                                                                  (a nil (s (a nil))))))))
                         (exp:end)))
                      :width 1220
                      :height 730))
  